package tool;

import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeMap;

/**
 *
 * @author linkai
 */
public class JavaCodeAnalyzer {
    
    private ArrayList<File> fileSet;
    private ArrayList<String> keyWords;
    private String[] keyWordsSet;
    private String path;
    private int numOfFile;
    private long lengthOfFile;
    private int rowOfFile;//代码总行数
    private int rowOfCode;//代码行数
    private int rowOfNote;//注释行数
    private int rowOfBlank;//空白行数

    /**
     * @return the numOfFile
     */
    public int getNumOfFile() {
        this.numOfFile = fileSet.size();
        return this.numOfFile;
    }

    /**
     * @return the lengthOfFile
     */
    public long getLengthOfFile() {
        lengthOfFile = 0;
        for (File e : fileSet) {
            lengthOfFile += e.length();
        }
        return lengthOfFile;
    }

    /**
     * @return the rowOfFile
     */
    public int getRowOfFile() {
        return rowOfFile;
    }

    /**
     * @return the rowOfCode
     */
    public int getRowOfCode() {
        return rowOfCode;
    }

    /**
     * @return the rowOfNote
     */
    public int getRowOfNote() {
        return rowOfNote;
    }

    /**
     * @return the rowOfBlank
     */
    public int getRowOfBlank() {
        return rowOfBlank;
    }

    public String getKeyWord() {
        Map<String, Integer> list = new TreeMap<>();
        for (String e : keyWordsSet) {
            list.put(e, 0);
        }
        for (String e : keyWords) {
            if (list.containsKey(e)) {
                list.put(e, list.get(e) + 1);
            } else {
                list.put(e, 1);
            }
        }
        List<Map.Entry<String, Integer>> alist = new ArrayList<>(list.entrySet());
        Collections.sort(alist, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                int flag = o2.getValue().compareTo(o1.getValue());
                if (flag == 0) {
                    return o2.getKey().compareTo(o1.getKey());
                } else {
                    return flag;
                }
            }
        });
        StringBuilder reString = new StringBuilder(200);
        String buil;
        for (Map.Entry<String, Integer> entry : alist) {
            buil = entry.getKey() + ":" + entry.getValue() + "次\r\n";
            reString.append(buil);
        }
        return reString.toString();

    }

    /**
     * calculate the row of code,note and blank
     *
     * @throws java.io.FileNotFoundException
     */
    public void codeAnalyzer() throws FileNotFoundException {
        for (File e : fileSet) {
            int allRow = calculateRow(e);
            int notesRow = calculateNotesRow(e);
            int blanksRow = calculateBlanksRow(e);
            calculateCodeRow(e, allRow, notesRow, blanksRow);
            findKeyWords(e);
        }
    }

    public void scannerFile(File dir, int level) {
        File[] childern = dir.listFiles();
        for (File e : childern) {
            if (e.isDirectory()) {
                scannerFile(e, level + 1);
            } else if (e.isFile()) {
                if (e.getName().matches("^.+" + "java" + "$")) {//判断后缀名是否为.java
                    fileSet.add(e);
                }
            }
        }
    }

    public void writeRecordToFile() throws IOException {
        String codePercent = String.format("%.2f", ((double) this.getRowOfCode() / this.getRowOfFile()) * 100);
        String notePercent = String.format("%.2f", ((double) this.getRowOfNote() / this.getRowOfFile()) * 100);
        String blankPercent = String.format("%.2f", ((double) this.getRowOfBlank() / this.getRowOfFile()) * 100);
        File file = new File("javaAnalyzer.txt");
        if (!file.exists()) {
            file.createNewFile();
        }
        try (FileWriter writer = new FileWriter(file, true); BufferedWriter buffwriter = new BufferedWriter(writer)) {
            String outdata;
            outdata = "目录名称:" + this.getPath() + "\r\n";
            buffwriter.write(outdata);
            outdata = "共有源文件:" + this.getNumOfFile() + "个 , 总的大小为:" + this.getLengthOfFile() + "字节\r\n";
            buffwriter.write(outdata);
            outdata = "源程序文件总行数:" + this.getRowOfFile() + " 其中:\r\n" + "代码行数:" + this.getRowOfCode() + ",占" + codePercent + "%\r\n"
                    + "注释行数:" + this.getRowOfNote() + ",占" + notePercent + "%\r\n" + "空白行数:" + this.getRowOfBlank() + ",占" + blankPercent + "%\r\n";
            buffwriter.write(outdata);
            buffwriter.write("\r\n");
            buffwriter.write(this.getKeyWord());
            buffwriter.write("\r\n");
            SimpleDateFormat time = new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss");
            outdata = "本次分析的时间:" + time.format(new Date());
            buffwriter.write(outdata);
            buffwriter.write("\r\n");
        }

        /*11个 , 总的大小为:16819字节
源程序文件总行数:565,其中:
代码行数:394,占69.73%
注释行数:109,占19.29%
空白行数:62,占10.97%*/
    }

    /**
     *
     * conductor
     */
    public JavaCodeAnalyzer() {
        this.lengthOfFile = 0;
        this.numOfFile = 0;
        this.rowOfBlank = 0;
        this.rowOfCode = 0;
        this.rowOfFile = 0;
        this.rowOfNote = 0;
        this.fileSet = new ArrayList<>();
        this.keyWords = new ArrayList<>();
        this.keyWordsSet = new String[]{
            "abstract", "double", "int", "super", "assert",
            "else", "interface", "switch", "boolean", "enum",
            "long", "synchronized", "break", "extends", "native",
            "this", "byte", "final", "new", "throw",
            "case", "finally", "package", "throws", "catch",
            "float", "private", "transient", "char", "for",
            "protected", "try", "class", "goto", "public",
            "void", "const", "if", "return", "volatile",
            "continue", "implements", "short", "while", "default",
            "import", "static", "do", "instanceof", "strictfp"
        };
    }

    private int calculateRow(File e) throws FileNotFoundException {
        Scanner in = new Scanner(e, "utf-8");
        int allLength = 0;
        while (in.hasNext()) {
            in.nextLine();
            allLength++;
        }
        this.rowOfFile += allLength;
        return allLength;
    }

    private int calculateBlanksRow(File e) throws FileNotFoundException {
        Scanner in = new Scanner(e, "utf-8");
        String test;
        String regex = "*/";
        int blankLength = 0;
        while (in.hasNext()) {
            test = in.nextLine().trim();
            if (isNote2(test) || isNote3(test)) {
                if (!(test.endsWith(regex))) {
                    while (in.hasNext() && (!(in.nextLine().endsWith(regex)))) {
                    }
                }
            }
            if (test.equals("")) {
                blankLength++;
            }
        }
        this.rowOfBlank += blankLength;
        return blankLength;
    }

    private int calculateNotesRow(File e) throws FileNotFoundException {
        Scanner in = new Scanner(e, "utf-8");
        String test;
        String regex = "*/";
        int notesRow = 0;
        while (in.hasNext()) {
            test = in.nextLine();
            if (isNote1(test)) {
                notesRow++;
            } else if (isNote2(test)) {
                notesRow++;
                if (!(test.endsWith(regex))) {
                    while (in.hasNext() && (!(in.nextLine().endsWith(regex)))) {
                        notesRow++;
                    }
                    notesRow++;
                }
            } else if (isNote3(test)) {
                if (!(test.endsWith(regex))) {
                    while (in.hasNext() && !(in.nextLine().endsWith(regex))) {
                        notesRow++;
                    }
                    notesRow++;
                }
            }
        }
        this.rowOfNote += notesRow;
        return notesRow;
    }

    private void calculateCodeRow(File e, int allRow, int notesRow, int blanksRow) {
        this.rowOfCode += allRow - notesRow - blanksRow;
    }

    private boolean isNote1(String test) {//以"//"开头的行
        String regex = "^//.*";
        return test.trim().matches(regex);
    }

    private boolean isNote2(String test) {//以"/*"开头的行*/
        String regex = "/*";
        return test.trim().startsWith(regex);
    }

    private boolean isNote3(String test) {//以代码开头，中间存在"/*"的行*/
        String regex = "/*";
        return (!test.trim().startsWith(regex)) && (test.trim().contains(regex));
    }

    private boolean isNote4(String test) {
        String regex = "//";
        return (!(test.trim().startsWith(regex))) && (test.trim().contains(regex));
    }

    private void collectKeyWord(String keyWords) {
        String[] keywordarray;
        keywordarray = keyWords.split(" |=|\\.|\\{|\\}|\\(|\\)|\\[|\\]|\\+|\\*|\\-|\\/|$|;|<|>");
        for (String e : keywordarray) {
            for (String key : keyWordsSet) {
                if (e.equals(key)) {
                    this.keyWords.add(e);
                }
            }
        }
    }

    private void findKeyWords(File e) throws FileNotFoundException {
        Scanner in = new Scanner(e, "utf-8");
        String test;
        while (in.hasNext()) {
            test = in.nextLine();
            if (isNote1(test)) {
            } else if (isNote2(test)) {
                if (!(test.endsWith("*/"))) {
                    while (in.hasNext() && (!(in.nextLine().endsWith("*/")))) {
                    }
                }
            } else if (isNote3(test)) {
                int indexOfFalg = test.trim().indexOf("/*");
                collectKeyWord(test.trim().substring(0, indexOfFalg));
                if (!(test.endsWith("*/"))) {
                    while (in.hasNext() && (!(in.nextLine().endsWith("*/")))) {
                    }
                }
            } else if (isNote4(test)) {
                int indexOfFalg = test.trim().indexOf("//");
                collectKeyWord(test.trim().substring(0, indexOfFalg));
            } else {
                collectKeyWord(test.trim());
            }
        }
    }

    /**
     * @return the path
     */
    public String getPath() {
        return path;
    }

    /**
     * @param path the path to set
     */
    public void setPath(String path) {
        this.path = path;
    }
}
